<%#
 Copyright 2013-2020 the original author or authors from the JHipster project.

 This file is part of the JHipster project, see https://www.jhipster.tech/
 for more information.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-%>
package <%= packageName %>.service;

import <%= packageName %>.config.Constants;
<%_ if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') { _%>
import <%= packageName %>.domain.Authority;
<%_ } _%>
<%_ if (databaseType !== 'no') { _%>
import <%= packageName %>.domain.<%= asEntity('User') %>;
    <%_ if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') { _%>
import <%= packageName %>.repository.AuthorityRepository;
        <%_ if (authenticationType === 'session' && !reactive) { _%>
import <%= packageName %>.repository.PersistentTokenRepository;
        <%_ } _%>
    <%_ } _%>
import <%= packageName %>.repository.UserRepository;
    <%_ if (searchEngine === 'elasticsearch') { _%>
import <%= packageName %>.repository.search.UserSearchRepository;
    <%_ } _%>
    <%_ if (authenticationType !== 'oauth2') { _%>
import <%= packageName %>.security.AuthoritiesConstants;
    <%_ } _%>
import <%= packageName %>.security.SecurityUtils;
<%_ } _%>
import <%= packageName %>.service.dto.<%= asDto('User') %>;
<%_ if (authenticationType !== 'oauth2') { _%>

import io.github.jhipster.security.RandomUtil;
<%_ } _%>

<%_ if (databaseType !== 'no') { _%>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
<%_ } _%>
<%_ if (cacheManagerIsAvailable === true) { _%>
import org.springframework.cache.CacheManager;
<%_ } _%>
<%_ if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') { _%>
    <%_ if (!reactive) { _%>
import org.springframework.data.domain.Page;
    <%_ } _%>
import org.springframework.data.domain.Pageable;
    <%_ if (authenticationType !== 'oauth2') { _%>
import org.springframework.scheduling.annotation.Scheduled;
    <%_ } _%>
<%_ } _%>
<%_ if (authenticationType === 'oauth2') { _%>
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
<%_ } _%>
<%_ if (authenticationType !== 'oauth2') { _%>
import org.springframework.security.crypto.password.PasswordEncoder;
<%_ } _%>
import org.springframework.stereotype.Service;
<%_ if (databaseType === 'sql') { _%>
import org.springframework.transaction.annotation.Transactional;
<%_ } _%>
<%_ if (reactive) { _%>
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
<%_ } _%>

<%_ if ((databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') && authenticationType === 'session'  && !reactive) { _%>
import java.time.LocalDate;
<%_ } _%>
<%_ if (databaseType !== 'no') { _%>
import java.time.Instant;
<%_ } _%>
<%_ if (databaseType === 'sql' && reactive && authenticationType !== 'oauth2') { _%>
import java.time.LocalDateTime;
import java.time.ZoneOffset;
<%_ } _%>
<%_ if (authenticationType !== 'oauth2' && (databaseType === 'sql' || databaseType === 'neo4j'  || databaseType === 'mongodb' || databaseType === 'couchbase')) { _%>
import java.time.temporal.ChronoUnit;
<%_ } _%>
import java.util.*;
<%_ if (!reactive || authenticationType === 'oauth2') { _%>
import java.util.stream.Collectors;
<%_ } _%>

/**
 * Service class for managing users.
 */
@Service
<%_ if (databaseType === 'sql' && !reactive) { _%>
@Transactional
<%_ } _%>
public class UserService {
<%_ if (databaseType !== 'no') { _%>

    private final Logger log = LoggerFactory.getLogger(UserService.class);

    private final UserRepository userRepository;
    <%_ if (authenticationType !== 'oauth2') { _%>

    private final PasswordEncoder passwordEncoder;
    <%_ } _%>
    <%_ if (searchEngine === 'elasticsearch') { _%>

    private final UserSearchRepository userSearchRepository;
    <%_ } _%>
    <%_ if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') { _%>
        <%_ if (authenticationType === 'session'  && !reactive) { _%>

    private final PersistentTokenRepository persistentTokenRepository;
        <%_ } _%>

    private final AuthorityRepository authorityRepository;
    <%_ } _%>
    <%_ if (cacheManagerIsAvailable === true) { _%>

    private final CacheManager cacheManager;
    <%_ } _%>

    public UserService(UserRepository userRepository<% if (authenticationType !== 'oauth2') { %>, PasswordEncoder passwordEncoder<% } %><% if (searchEngine === 'elasticsearch') { %>, UserSearchRepository userSearchRepository<% } %><% if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') { %><% if (authenticationType === 'session'  && !reactive) { %>, PersistentTokenRepository persistentTokenRepository<% } %>, AuthorityRepository authorityRepository<% } %><% if (cacheManagerIsAvailable === true) { %>, CacheManager cacheManager<% } %>) {
        this.userRepository = userRepository;
        <%_ if (authenticationType !== 'oauth2') { _%>
        this.passwordEncoder = passwordEncoder;
        <%_ } _%>
        <%_ if (searchEngine === 'elasticsearch') { _%>
        this.userSearchRepository = userSearchRepository;
        <%_ } _%>
        <%_ if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') { _%>
            <%_ if (authenticationType === 'session'  && !reactive) { _%>
        this.persistentTokenRepository = persistentTokenRepository;
            <%_ } _%>
        this.authorityRepository = authorityRepository;
        <%_ } _%>
        <%_ if (cacheManagerIsAvailable === true) { _%>
        this.cacheManager = cacheManager;
        <%_ } _%>
    }
<%_ if (authenticationType !== 'oauth2') { _%>

    <%_ if (databaseType === 'sql' && reactive) { _%>
    @Transactional
    <%_ } _%>
    public <% if (reactive) { %>Mono<% } else { %>Optional<% } %><<%= asEntity('User') %>> activateRegistration(String key) {
        log.debug("Activating user for activation key {}", key);
        return userRepository.findOneByActivationKey(key)
            .<% if (reactive) { %>flatMap<% } else { %>map<% } %>(user -> {
                // activate given user for the registration key.
                user.setActivated(true);
                user.setActivationKey(null);
            <%_ if (!reactive) { _%>
                <%_ if (['mongodb', 'neo4j', 'couchbase', 'cassandra'].includes(databaseType)) { _%>
                userRepository.save(user);
                <%_ } _%>
                <%_ if (searchEngine === 'elasticsearch') { _%>
                userSearchRepository.save(user);
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
                this.clearUserCaches(user);
                <%_ } _%>
                log.debug("Activated user: {}", user);
                return user;
            });
            <%_ } else { _%>
                return saveUser(user);
            })
                <%_ if (searchEngine === 'elasticsearch') { _%>
            .flatMap(user -> userSearchRepository.save(user).thenReturn(user))
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
            .doOnNext(this::clearUserCaches)
                <%_ } _%>
            .doOnNext(user -> log.debug("Activated user: {}", user));
            <%_ } _%>
    }

    <%_ if (databaseType === 'sql' && reactive) { _%>
    @Transactional
    <%_ } _%>
    public <% if (reactive) { %>Mono<% } else { %>Optional<% } %><<%= asEntity('User') %>> completePasswordReset(String newPassword, String key) {
        log.debug("Reset user password for reset key {}", key);
        return userRepository.findOneByResetKey(key)
            .filter(user -> user.getResetDate().isAfter(Instant.now().minusSeconds(86400)))
            <%_ if (!reactive) { _%>
            .map(user -> {
                user.setPassword(passwordEncoder.encode(newPassword));
                user.setResetKey(null);
                user.setResetDate(null);
                <%_ if (['mongodb', 'neo4j', 'couchbase', 'cassandra'].includes(databaseType)) { _%>
                userRepository.save(user);
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
                this.clearUserCaches(user);
                <%_ } _%>
                return user;
            });
            <%_ } else { _%>
            .publishOn(Schedulers.boundedElastic())
            .map(user -> {
                user.setPassword(passwordEncoder.encode(newPassword));
                user.setResetKey(null);
                user.setResetDate(null);
                return user;
            })
            .flatMap(this::saveUser)<% if (cacheManagerIsAvailable === true) { %>
            .doOnNext(this::clearUserCaches)<% } %>;
            <%_ } _%>
    }

    <%_ if (databaseType === 'sql' && reactive) { _%>
    @Transactional
    <%_ } _%>
    public <% if (reactive) { %>Mono<% } else { %>Optional<% } %><<%= asEntity('User') %>> requestPasswordReset(String mail) {
        return userRepository.findOneByEmailIgnoreCase(mail)
            .filter(<%= asEntity('User') %>::isActivated)
            <%_ if (!reactive) { _%>
            .map(user -> {
                user.setResetKey(RandomUtil.generateResetKey());
                user.setResetDate(Instant.now());
                <%_ if (['mongodb', 'neo4j', 'couchbase', 'cassandra'].includes(databaseType)) { _%>
                userRepository.save(user);
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
                this.clearUserCaches(user);
                <%_ } _%>
                return user;
            });
            <%_ } else { _%>
            .publishOn(Schedulers.boundedElastic())
            .map(user -> {
                user.setResetKey(RandomUtil.generateResetKey());
                user.setResetDate(Instant.now());
                return user;
            })
            .flatMap(this::saveUser)<% if (cacheManagerIsAvailable === true) { %>
            .doOnNext(this::clearUserCaches)<% } %>;
            <%_ } _%>
    }

    <%_ if (databaseType === 'sql' && reactive) { _%>
    @Transactional
    <%_ } _%>
    public <% if (reactive) { %>Mono<<%= asEntity('User') %>><% } else { %><%= asEntity('User') %><% } %> registerUser(<%= asDto('User') %> userDTO, String password) {
    <%_ if (!reactive) { _%>
        userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).ifPresent(existingUser -> {
            boolean removed = removeNonActivatedUser(existingUser);
            if (!removed) {
                throw new UsernameAlreadyUsedException();
            }
        });
        userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()).ifPresent(existingUser -> {
            boolean removed = removeNonActivatedUser(existingUser);
            if (!removed) {
                throw new EmailAlreadyUsedException();
            }
        });
        <%= asEntity('User') %> newUser = new <%= asEntity('User') %>();
        <%_ if (databaseType === 'cassandra') { _%>
        newUser.setId(UUID.randomUUID().toString());
        <%_ } _%>
        String encryptedPassword = passwordEncoder.encode(password);
        newUser.setLogin(userDTO.getLogin().toLowerCase());
        // new user gets initially a generated password
        newUser.setPassword(encryptedPassword);
        newUser.setFirstName(userDTO.getFirstName());
        newUser.setLastName(userDTO.getLastName());
        if (userDTO.getEmail() != null) {
            newUser.setEmail(userDTO.getEmail().toLowerCase());
        }
        <%_ if (['sql', 'mongodb', 'couchbase', 'neo4j'].includes(databaseType)) { _%>
        newUser.setImageUrl(userDTO.getImageUrl());
        <%_ } _%>
        newUser.setLangKey(userDTO.getLangKey());
        // new user is not active
        newUser.setActivated(false);
        // new user gets registration key
        newUser.setActivationKey(RandomUtil.generateActivationKey());
        <%_ if (['sql', 'mongodb', 'neo4j'].includes(databaseType)) { _%>
        Set<Authority> authorities = new HashSet<>();
        authorityRepository.findById(AuthoritiesConstants.USER).ifPresent(authorities::add);
        <%_ } else { _%>
        Set<String> authorities = new HashSet<>();
        authorities.add(AuthoritiesConstants.USER);
        <%_ } _%>
        newUser.setAuthorities(authorities);
        userRepository.save(newUser);
        <%_ if (searchEngine === 'elasticsearch') { _%>
        userSearchRepository.save(newUser);
        <%_ } _%>
        <%_ if (cacheManagerIsAvailable === true) { _%>
        this.clearUserCaches(newUser);
        <%_ } _%>
        log.debug("Created Information for User: {}", newUser);
        return newUser;
    <%_ } else { // reactive _%>
        return userRepository.findOneByLogin(userDTO.getLogin().toLowerCase())
            .flatMap(existingUser -> {
                if (!existingUser.isActivated()) {
                    <%_ if (cacheManagerIsAvailable === true) { _%>
                    this.clearUserCaches(existingUser);
                    <%_ } _%>
                    return userRepository.delete(existingUser);
                } else {
                    return Mono.error(new UsernameAlreadyUsedException());
                }
            })
            .then(userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()))
            .flatMap(existingUser -> {
                if (!existingUser.isActivated()) {
                    <%_ if (cacheManagerIsAvailable === true) { _%>
                    this.clearUserCaches(existingUser);
                    <%_ } _%>
                    return userRepository.delete(existingUser);
                } else {
                    return Mono.error(new EmailAlreadyUsedException());
                }
            })
            .publishOn(Schedulers.boundedElastic())
            .then(Mono.fromCallable(() -> {
                User newUser = new <%= asEntity('User') %>();
                <%_ if (databaseType === 'cassandra') { _%>
                newUser.setId(UUID.randomUUID().toString());
                <%_ } _%>
                String encryptedPassword = passwordEncoder.encode(password);
                newUser.setLogin(userDTO.getLogin().toLowerCase());
                // new user gets initially a generated password
                newUser.setPassword(encryptedPassword);
                newUser.setFirstName(userDTO.getFirstName());
                newUser.setLastName(userDTO.getLastName());
                if (userDTO.getEmail() != null) {
                    newUser.setEmail(userDTO.getEmail().toLowerCase());
                }
                <%_ if (['sql', 'mongodb', 'neo4j', 'couchbase'].includes(databaseType)) { _%>
                newUser.setImageUrl(userDTO.getImageUrl());
                <%_ } _%>
                newUser.setLangKey(userDTO.getLangKey());
                // new user is not active
                newUser.setActivated(false);
                // new user gets registration key
                newUser.setActivationKey(RandomUtil.generateActivationKey());
                return newUser;
            }))
            .flatMap(newUser -> {
                Set<<% if (['sql', 'mongodb', 'neo4j'].includes(databaseType)) { %>Authority<% } else { %>String<% } %>> authorities = new HashSet<>();
                <%_ if (['sql', 'mongodb', 'neo4j'].includes(databaseType)) { _%>
                return authorityRepository.findById(AuthoritiesConstants.USER)
                    .map(authorities::add)
                    .thenReturn(newUser)
                    .doOnNext(user -> user.setAuthorities(authorities))
                    .flatMap(this::saveUser)
                <%_ } else { _%>
                authorities.add(AuthoritiesConstants.USER);
                newUser.setAuthorities(authorities);
                return saveUser(newUser)
                <%_ } _%>
                    <%_ if (searchEngine === 'elasticsearch') { _%>
                    .flatMap(user -> userSearchRepository.save(user).thenReturn(user))
                    <%_ } _%>
                    <%_ if (cacheManagerIsAvailable === true) { _%>
                    .doOnNext(this::clearUserCaches)
                    <%_ } _%>
                    .doOnNext(user -> log.debug("Created Information for User: {}", user));
            });
    <%_ } _%>
    }

    <%_ if (!reactive) { _%>
    private boolean removeNonActivatedUser(<%= asEntity('User') %> existingUser) {
        if (existingUser.isActivated()) {
             return false;
        }
        userRepository.delete(existingUser);
        <%_ if (databaseType === 'sql') { _%>
        userRepository.flush();
        <%_ } _%>
        <%_ if (cacheManagerIsAvailable === true) { _%>
        this.clearUserCaches(existingUser);
        <%_ } _%>
        return true;
    }

    <%_ } _%>
    <%_ if (databaseType === 'sql' && reactive) { _%>
    @Transactional
    <%_ } _%>
    public <% if (reactive) { %>Mono<<%= asEntity('User') %>><% } else { %><%= asEntity('User') %><% } %> createUser(<%= asDto('User') %> userDTO) {
        <%= asEntity('User') %> user = new <%= asEntity('User') %>();<% if (databaseType === 'cassandra') { %>
        user.setId(UUID.randomUUID().toString());<% } %>
        user.setLogin(userDTO.getLogin().toLowerCase());
        user.setFirstName(userDTO.getFirstName());
        user.setLastName(userDTO.getLastName());
        if (userDTO.getEmail() != null) {
            user.setEmail(userDTO.getEmail().toLowerCase());
        }
        <%_ if (['sql', 'couchbase', 'mongodb', 'neo4j'].includes(databaseType)) { _%>
        user.setImageUrl(userDTO.getImageUrl());
        <%_ } _%>
        if (userDTO.getLangKey() == null) {
            user.setLangKey(Constants.DEFAULT_LANGUAGE); // default language
        } else {
            user.setLangKey(userDTO.getLangKey());
        }
    <%_ if (!reactive) { _%>
        String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
        user.setPassword(encryptedPassword);
        user.setResetKey(RandomUtil.generateResetKey());
        user.setResetDate(Instant.now());
        user.setActivated(true);
        <%_ if (['sql', 'mongodb', 'neo4j'].includes(databaseType)) { _%>
        if (userDTO.getAuthorities() != null) {
            Set<Authority> authorities = userDTO.getAuthorities().stream()
                .map(authorityRepository::findById)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toSet());
            user.setAuthorities(authorities);
        }
        <%_ } else { _%>
        user.setAuthorities(userDTO.getAuthorities());
        <%_ } _%>
        userRepository.save(user);
        <%_ if (searchEngine === 'elasticsearch') { _%>
        userSearchRepository.save(user);
        <%_ } _%>
        <%_ if (cacheManagerIsAvailable === true) { _%>
        this.clearUserCaches(user);
        <%_ } _%>
        log.debug("Created Information for User: {}", user);
        return user;
    <%_ } else { _%>
        <%_ if (['sql', 'mongodb', 'neo4j'].includes(databaseType)) { _%>
        return Flux.fromIterable(userDTO.getAuthorities() != null ? userDTO.getAuthorities() : new HashSet<>())
            .flatMap(authorityRepository::findById)
            .doOnNext(authority -> user.getAuthorities().add(authority))
            .then(Mono.just(user))
        <%_ } else { _%>
        user.setAuthorities(userDTO.getAuthorities());
        return Mono.just(user)
        <%_ } _%>
            .publishOn(Schedulers.boundedElastic())
            .map(newUser -> {
                String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
                newUser.setPassword(encryptedPassword);
                newUser.setResetKey(RandomUtil.generateResetKey());
                newUser.setResetDate(Instant.now());
                newUser.setActivated(true);
                return newUser;
            })
            .flatMap(this::saveUser)
            <%_ if (searchEngine === 'elasticsearch') { _%>
            .flatMap(user1 -> userSearchRepository.save(user1).thenReturn(user1))
            <%_ } _%>
            <%_ if (cacheManagerIsAvailable === true) { _%>
            .doOnNext(this::clearUserCaches)
            <%_ } _%>
            .doOnNext(user1 -> log.debug("Created Information for User: {}", user1));
    <%_ } _%>
    }

    /**
     * Update all information for a specific user, and return the modified user.
     *
     * @param userDTO user to update.
     * @return updated user.
     */
    <%_ if (databaseType === 'sql' && reactive) { _%>
    @Transactional
    <%_ } _%>
    public <% if (reactive) { %>Mono<% } else { %>Optional<% } %><<%= asDto('User') %>> updateUser(<%= asDto('User') %> userDTO) {
        <%_ if (!reactive) { _%>
        return Optional.of(userRepository
            .findById(userDTO.getId()))
            .filter(Optional::isPresent)
            .map(Optional::get)
            .map(user -> {
        <%_ } else { _%>
        return userRepository.findById(userDTO.getId())
            .<%_ if (['sql', 'mongodb'].includes(databaseType)) { _%>flatMap<% } else { %>map<% } %>(user -> {
        <%_ } _%>
                <%_ if (databaseType === 'couchbase') { _%>
                if (!user.getLogin().equals(userDTO.getLogin())) {
                    userRepository.deleteById(userDTO.getId());
                }
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
                this.clearUserCaches(user);
                <%_ } _%>
                user.setLogin(userDTO.getLogin().toLowerCase());
                user.setFirstName(userDTO.getFirstName());
                user.setLastName(userDTO.getLastName());
                if (userDTO.getEmail() != null) {
                    user.setEmail(userDTO.getEmail().toLowerCase());
                }
                <%_ if (['sql', 'mongodb', 'neo4j', 'couchbase'].includes(databaseType)) { _%>
                user.setImageUrl(userDTO.getImageUrl());
                <%_ } _%>
                user.setActivated(userDTO.isActivated());
                user.setLangKey(userDTO.getLangKey());
                <%_ if (['sql', 'mongodb', 'neo4j'].includes(databaseType)) { _%>
                Set<Authority> managedAuthorities = user.getAuthorities();
                managedAuthorities.clear();
                <%_ } else { // Cassandra & Couchbase _%>
                user.setAuthorities(userDTO.getAuthorities());
                <%_ } _%>
        <%_ if (!reactive) { _%>
                <%_ if (['sql', 'mongodb', 'neo4j'].includes(databaseType)) { _%>
                userDTO.getAuthorities().stream()
                    .map(authorityRepository::findById)
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .forEach(managedAuthorities::add);
                <%_ } _%>
                <%_ if (['mongodb', 'neo4j', 'couchbase', 'cassandra'].includes(databaseType)) { _%>
                userRepository.save(user);
                <%_ } _%>
                <%_ if (searchEngine === 'elasticsearch') { _%>
                userSearchRepository.save(user);
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
                this.clearUserCaches(user);
                <%_ } _%>
                log.debug("Changed Information for User: {}", user);
                return user;
            })
        <%_ } else { // reactive _%>
                <%_ if (['sql', 'mongodb'].includes(databaseType)) { _%>
                return Flux.fromIterable(userDTO.getAuthorities())
                    .flatMap(authorityRepository::findById)
                    .map(managedAuthorities::add)
                    .then(Mono.just(user));
                <%_ } else { _%>
                return user;
                <%_ } _%>
            })
            .flatMap(this::saveUser)
            <%_ if (searchEngine === 'elasticsearch') { _%>
            .flatMap(user -> userSearchRepository.save(user).thenReturn(user))
            <%_ } _%>
            <%_ if (cacheManagerIsAvailable === true) { _%>
            .doOnNext(this::clearUserCaches)
            <%_ } _%>
            .doOnNext(user -> log.debug("Changed Information for User: {}", user))
        <%_ } _%>
            .map(<%= asDto('User') %>::new);
    }

    <%_ if (!reactive) { _%>
    public void deleteUser(String login) {
        userRepository.findOneByLogin(login).ifPresent(user -> {
            userRepository.delete(user);
            <%_ if (searchEngine === 'elasticsearch') { _%>
            userSearchRepository.delete(user);
            <%_ } _%>
            <%_ if (cacheManagerIsAvailable === true) { _%>
            this.clearUserCaches(user);
            <%_ } _%>
            log.debug("Deleted User: {}", user);
        });
    }
    <%_ } else { _%>
        <%_ if (databaseType === 'sql') { _%>
    @Transactional
        <%_ } _%>
    public Mono<Void> deleteUser(String login) {
        return userRepository.findOneByLogin(login)
            .flatMap(user -> userRepository.delete(user).thenReturn(user))
            <%_ if (searchEngine === 'elasticsearch') { _%>
            .flatMap(user -> userSearchRepository.delete(user).thenReturn(user))
            <%_ } _%>
            <%_ if (cacheManagerIsAvailable === true) { _%>
            .doOnNext(this::clearUserCaches)
            <%_ } _%>
            .doOnNext(user -> log.debug("Deleted User: {}", user))
            .then();
    }
    <%_ } _%>
<%_ } // authenticationType !== 'oauth2' _%>

    /**
     * Update basic information (first name, last name, email, language) for the current user.
     *
     * @param firstName first name of user.
     * @param lastName  last name of user.
     * @param email     email id of user.
     * @param langKey   language key.
     <%_ if (['sql', 'mongodb', 'couchbase', 'neo4j'].includes(databaseType)) { _%>
     * @param imageUrl  image URL of user.
     <%_ } _%>
     */
    <%_ if (databaseType === 'sql' && reactive) { _%>
    @Transactional
    <%_ } _%>
    public <% if (reactive) { %>Mono<Void><% } else { %>void<% } %> updateUser(String firstName, String lastName, String email, String langKey<% if (['sql', 'mongodb', 'couchbase', 'neo4j'].includes(databaseType)) { %>, String imageUrl<% } %>) {
        <% if (reactive) { %>return <% } %>SecurityUtils.getCurrentUserLogin()
            .flatMap(userRepository::findOneByLogin)
            .<% if (reactive) { %>flatMap<% } else { %>ifPresent<% } %>(user -> {
                user.setFirstName(firstName);
                user.setLastName(lastName);
                if (email != null) {
                    user.setEmail(email.toLowerCase());
                }
                user.setLangKey(langKey);
                <%_ if (['sql', 'mongodb', 'couchbase', 'neo4j'].includes(databaseType)) { _%>
                user.setImageUrl(imageUrl);
                <%_ } _%>
            <%_ if (!reactive) { _%>
                <%_ if (['mongodb', 'neo4j', 'couchbase', 'cassandra'].includes(databaseType)) { _%>
                userRepository.save(user);
                <%_ } _%>
                <%_ if (searchEngine === 'elasticsearch') { _%>
                userSearchRepository.save(user);
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
                this.clearUserCaches(user);
                <%_ } _%>
                log.debug("Changed Information for User: {}", user);
            });
            <%_ } else { _%>
                return saveUser(user);
            })
                <%_ if (searchEngine === 'elasticsearch') { _%>
            .flatMap(user -> userSearchRepository.save(user).thenReturn(user))
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
            .doOnNext(this::clearUserCaches)
                <%_ } _%>
            .doOnNext(user -> log.debug("Changed Information for User: {}", user))
            .then();
            <%_ } _%>
    }

<%_ if (reactive) { _%>
    <%_ if (databaseType === 'sql' && authenticationType === 'oauth2') { _%>
    @Transactional
    Mono<User> saveUser(User user) {
        return saveUser(user, false);
    }

    <%_ } _%>
    <%_ if (databaseType === 'sql') { _%>
    @Transactional
    <%_ } _%>
    <% if (databaseType !== 'sql') { %>private <% } %>Mono<<%= asEntity('User') %>> saveUser(<%= asEntity('User') %> user<% if (databaseType === 'sql' && authenticationType === 'oauth2') { %>, boolean forceCreate<% } %>) {
        <%_ if (databaseType === 'cassandra') { _%>
        return userRepository.save(user);
        <%_ } else { _%>
        return SecurityUtils.getCurrentUserLogin()
            .switchIfEmpty(Mono.just(Constants.SYSTEM_ACCOUNT))
            .flatMap(login -> {
                if (user.getCreatedBy() == null) {
                    user.setCreatedBy(login);
                }
                user.setLastModifiedBy(login);
            <%_ if (databaseType === 'sql') { _%>
                // Saving the relationship can be done in an entity callback
                // once https://github.com/spring-projects/spring-data-r2dbc/issues/215 is done
                <%_ if (authenticationType === 'oauth2') { _%>
                Mono<User> persistedUser;
                if (forceCreate) {
                    persistedUser = userRepository.create(user);
                } else {
                    persistedUser = userRepository.save(user);
                }
                return persistedUser
                <%_ } else { _%>
                return userRepository.save(user)
                <%_ } _%>
                    .flatMap(savedUser ->
                        Flux.fromIterable(user.getAuthorities())
                            .flatMap(authority -> userRepository.saveUserAuthority(savedUser.getId(), authority.getName()))
                            .then(Mono.just(savedUser))
                    );
            <%_ } else { _%>
                return userRepository.save(user);
            <%_ } _%>
            });
        <%_ } _%>
    }
<%_ } _%>
<%_ if (authenticationType !== 'oauth2') { _%>

    <%_ if (databaseType === 'sql') { _%>
    @Transactional
    <%_ } _%>
    public <% if (reactive) { %>Mono<Void><% } else { %>void<% } %> changePassword(String currentClearTextPassword, String newPassword) {
        <% if (reactive) { %>return <% } %>SecurityUtils.getCurrentUserLogin()
            .flatMap(userRepository::findOneByLogin)
            <%_ if (reactive) { _%>
            .publishOn(Schedulers.boundedElastic())
            .map(user -> {
            <%_ } else { _%>
            .ifPresent(user -> {
            <%_ } _%>
                String currentEncryptedPassword = user.getPassword();
                if (!passwordEncoder.matches(currentClearTextPassword, currentEncryptedPassword)) {
                    throw new InvalidPasswordException();
                }
                String encryptedPassword = passwordEncoder.encode(newPassword);
                user.setPassword(encryptedPassword);
            <%_ if (!reactive) { _%>
                <%_ if (['mongodb', 'neo4j', 'couchbase', 'cassandra'].includes(databaseType)) { _%>
                userRepository.save(user);
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
                this.clearUserCaches(user);
                <%_ } _%>
                log.debug("Changed password for User: {}", user);
            });
            <%_ } else { _%>
                return user;
            })
            .flatMap(this::saveUser)
                <%_ if (cacheManagerIsAvailable === true) { _%>
            .doOnNext(this::clearUserCaches)
                <%_ } _%>
            .doOnNext(user -> log.debug("Changed password for User: {}", user))
            .then();
            <%_ } _%>
    }
<%_ } _%>

    <%_ if (databaseType === 'sql') { _%>
    @Transactional(readOnly = true)
    <%_ } _%>
    <%_ if (['sql', 'mongodb', 'neo4j', 'couchbase'].includes(databaseType)) { _%>
    public <% if (reactive) { %>Flux<% } else { %>Page<% } %><<%= asDto('User') %>> getAllManagedUsers(Pageable pageable) {
        return userRepository.findAllByLoginNot(pageable, Constants.ANONYMOUS_USER).map(<%= asDto('User') %>::new);
    }
        <%_ if (reactive) { _%>

    <%_ if (databaseType === 'sql') { _%>
    @Transactional(readOnly = true)
    <%_ } _%>
    public Mono<Long> countManagedUsers() {
        return userRepository.countAllByLoginNot(Constants.ANONYMOUS_USER);
    }
        <%_ } _%>
    <%_ } else { // Cassandra _%>
    public <% if (reactive) { %>Flux<% } else { %>List<% } %><<%= asDto('User') %>> getAllManagedUsers() {
        return userRepository.findAll()<% if (!reactive) { %>.stream()<% } %>
            .filter(user -> !Constants.ANONYMOUS_USER.equals(user.getLogin()))
            .map(<%= asDto('User') %>::new)<% if (!reactive) { %>
            .collect(Collectors.toList())<% } %>;
    }
    <%_ } _%>

    <%_ if (databaseType === 'sql') { _%>
    @Transactional(readOnly = true)
    <%_ } _%>
    public <% if (reactive) { %>Mono<% } else { %>Optional<% } %><<%= asEntity('User') %>> getUserWithAuthoritiesByLogin(String login) {
        <%_ if (databaseType === 'sql') { _%>
        return userRepository.findOneWithAuthoritiesByLogin(login);
        <%_ } else { // MongoDB, Couchbase and Cassandra _%>
        return userRepository.findOneByLogin(login);
        <%_ } _%>
    }

    <%_ if (authenticationType !== 'oauth2') { _%>
        <%_ if (databaseType === 'sql') { _%>
    @Transactional(readOnly = true)
        <%_ } _%>
    public <% if (reactive) { %>Mono<% } else { %>Optional<% } %><<%= asEntity('User') %>> getUserWithAuthorities() {
        return SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOne<% if (databaseType === 'sql') { %>WithAuthorities<% } %>ByLogin);
    }
    <%_ } _%>
    <%_ if ((databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') && authenticationType === 'session'  && !reactive) { _%>

    /**
     * Persistent Token are used for providing automatic authentication, they should be automatically deleted after
     * 30 days.
     * <p>
     * This is scheduled to get fired everyday, at midnight.
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void removeOldPersistentTokens() {
        LocalDate now = LocalDate.now();
        persistentTokenRepository.findByTokenDateBefore(now.minusMonths(1)).forEach(token -> {
            log.debug("Deleting token {}", token.getSeries());<% if (databaseType === 'sql') { %>
            <%= asEntity('User') %> user = token.getUser();
            user.getPersistentTokens().remove(token);<% } %>
            persistentTokenRepository.delete(token);
        });
    }
    <%_ } _%>
    <%_ if (authenticationType !== 'oauth2' && (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase')) { _%>

    /**
     * Not activated users should be automatically deleted after 3 days.
     * <p>
     * This is scheduled to get fired everyday, at 01:00 (am).
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public void removeNotActivatedUsers() {
        <%_ if (!reactive) { _%>
        userRepository
            .findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(Instant.now().minus(3, ChronoUnit.DAYS))
            .forEach(user -> {
                log.debug("Deleting not activated user {}", user.getLogin());
                userRepository.delete(user);
                <%_ if (searchEngine === 'elasticsearch') { _%>
                userSearchRepository.delete(user);
                <%_ } _%>
                <%_ if (cacheManagerIsAvailable === true) { _%>
                this.clearUserCaches(user);
                <%_ } _%>
            });
        <%_ } else { _%>
        removeNotActivatedUsersReactively().blockLast();
    }

    <%_ if (databaseType === 'sql') { _%>
    @Transactional
    <%_ } _%>
    public Flux<User> removeNotActivatedUsersReactively() {
        return userRepository
            .findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(<%_ if (databaseType === 'sql') { _%>LocalDateTime.ofInstant(Instant.now().minus(3, ChronoUnit.DAYS), ZoneOffset.UTC)<%_ } else { _%>Instant.now().minus(3, ChronoUnit.DAYS)<%_ } _%>)
            .flatMap(user -> userRepository.delete(user).thenReturn(user))
            <%_ if (searchEngine === 'elasticsearch') { _%>
            .flatMap(user -> userSearchRepository.delete(user).thenReturn(user))
            <%_ } _%>
            <%_ if (cacheManagerIsAvailable === true) { _%>
            .doOnNext(this::clearUserCaches)
            <%_ } _%>
            .doOnNext(user -> log.debug("Deleted User: {}", user));
            <%_ } _%>
    }
    <%_ } _%>
    <%_ if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') { _%>

    /**
     * Gets a list of all the authorities.
     * @return a list of all the authorities.
     */
    <%_ if (databaseType === 'sql') { _%>
    @Transactional(readOnly = true)
    <%_ } _%>
    public <% if (reactive) { %>Flux<% } else { %>List<% } %><String> getAuthorities() {
        return authorityRepository.findAll()<% if (!reactive) { %>.stream()<% } %>.map(Authority::getName)<% if (!reactive) { %>.collect(Collectors.toList())<% } %>;
    }
    <%_ } _%>
    <%_ if (authenticationType === 'oauth2') { _%>

    private <% if (reactive) { %>Mono<<%= asEntity('User') %>><% } else { %><%= asEntity('User') %><% } %> syncUserWithIdP(Map<String, Object> details, <%= asEntity('User') %> user) {
        // save authorities in to sync user roles/groups between IdP and JHipster's local database
        <%_ if (!reactive) { _%>
        Collection<String> dbAuthorities = getAuthorities();
        <%_ if (databaseType !== 'couchbase') { _%>
        Collection<String> userAuthorities =
            user.getAuthorities().stream().map(Authority::getName).collect(Collectors.toList());
        <%_ } else { _%>
        Collection<String> userAuthorities = user.getAuthorities();
        <%_ } _%>
        for (String authority : userAuthorities) {
            if (!dbAuthorities.contains(authority)) {
                log.debug("Saving authority '{}' in local database", authority);
                Authority authorityToSave = new Authority();
                authorityToSave.setName(authority);
                authorityRepository.save(authorityToSave);
            }
        }
        // save account in to sync users between IdP and JHipster's local database
        Optional<<%= asEntity('User') %>> existingUser = userRepository.findOneByLogin(user.getLogin());
        if (existingUser.isPresent()) {
            // if IdP sends last updated information, use it to determine if an update should happen
            if (details.get("updated_at") != null) {
                Instant dbModifiedDate = existingUser.get().getLastModifiedDate();
                Instant idpModifiedDate = (Instant) details.get("updated_at");
                if (idpModifiedDate.isAfter(dbModifiedDate)) {
                    log.debug("Updating user '{}' in local database", user.getLogin());
                    updateUser(user.getFirstName(), user.getLastName(), user.getEmail(),
                        user.getLangKey(), user.getImageUrl());
                }
                // no last updated info, blindly update
            } else {
                log.debug("Updating user '{}' in local database", user.getLogin());
                updateUser(user.getFirstName(), user.getLastName(), user.getEmail(),
                    user.getLangKey(), user.getImageUrl());
            }
        } else {
            log.debug("Saving user '{}' in local database", user.getLogin());
            userRepository.save(user);
            <%_ if (cacheManagerIsAvailable === true) { _%>
            this.clearUserCaches(user);
            <%_ } _%>
        }
        return user;
        <%_ } else { _%>
        Collection<String> userAuthorities =
            user.getAuthorities().stream()<% if (databaseType !== 'couchbase') { %>.map(Authority::getName)<% } %>.collect(Collectors.toList());

        return getAuthorities().collectList()
            .flatMapMany(dbAuthorities -> {
                List<Authority> authoritiesToSave = userAuthorities.stream()
                    .filter(authority -> !dbAuthorities.contains(authority))
                    .map(authority -> {
                        Authority authorityToSave = new Authority();
                        authorityToSave.setName(authority);
                        return authorityToSave;
                    })
                    .collect(Collectors.toList());
                return Flux.fromIterable(authoritiesToSave);
            })
            .doOnNext(authority -> log.debug("Saving authority '{}' in local database", authority))
            .flatMap(authorityRepository::save)
            .then(userRepository.findOneByLogin(user.getLogin()))
            .switchIfEmpty(<% if (authenticationType === 'oauth2' && databaseType === 'sql') { %>saveUser(user, true)<% } else { %>userRepository.save(user)<% } %>)
            .flatMap(existingUser -> {
                // if IdP sends last updated information, use it to determine if an update should happen
                if (details.get("updated_at") != null) {
                    Instant dbModifiedDate = existingUser.getLastModifiedDate();
                    Instant idpModifiedDate = (Instant) details.get("updated_at");
                    if (idpModifiedDate.isAfter(dbModifiedDate)) {
                        log.debug("Updating user '{}' in local database", user.getLogin());
                        return updateUser(user.getFirstName(), user.getLastName(), user.getEmail(),
                            user.getLangKey(), user.getImageUrl());
                    }
                    // no last updated info, blindly update
                } else {
                    log.debug("Updating user '{}' in local database", user.getLogin());
                    return updateUser(user.getFirstName(), user.getLastName(), user.getEmail(),
                        user.getLangKey(), user.getImageUrl());
                }
                return Mono.empty();
            })
            .thenReturn(user);
        <%_ } _%>
    }
    <%_ } _%>

<%_ } // databaseType !== 'no' _%>
    <%_ if (authenticationType === 'oauth2') { _%>
    /**
     * Returns the user from an OAuth 2.0 login or resource server with JWT.
        <%_ if (databaseType !== 'no') { _%>
     * Synchronizes the user in the local repository.
        <%_ } _%>
     *
     * @param authToken the authentication token.
     * @return the user from the authentication.
     */
        <%_ if (databaseType === 'sql') { _%>
    @Transactional
        <%_ } _%>
    public <% if (!reactive) { %>UserDTO<% } else { %>Mono<UserDTO><% } %> getUserFromAuthentication(AbstractAuthenticationToken authToken) {
        Map<String, Object> attributes;
        if (authToken instanceof OAuth2AuthenticationToken) {
            attributes = ((OAuth2AuthenticationToken) authToken).getPrincipal().getAttributes();
        } else if (authToken instanceof JwtAuthenticationToken) {
            attributes = ((JwtAuthenticationToken) authToken).getTokenAttributes();
        } else {
            throw new IllegalArgumentException("AuthenticationToken is not OAuth2 or JWT!");
        }
        <%= databaseType === 'no' ? asDto('User') : asEntity('User') %> user = getUser(attributes);
        user.setAuthorities(authToken.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            <%_ if (['sql', 'mongodb', 'neo4j'].includes(databaseType)) { _%>
            .map(authority -> {
                Authority auth = new Authority();
                auth.setName(authority);
                return auth;
            })
            <%_ } _%>
            .collect(Collectors.toSet()));
        return <% if (databaseType !== 'no' && !reactive) { %>new UserDTO(syncUserWithIdP(attributes, user))<% } else if (!reactive) { %>user<% } %><% if (databaseType === 'no' && reactive) { %>Mono.just(user)<% } else if (reactive) { %>syncUserWithIdP(attributes, user).flatMap(u -> Mono.just(new UserDTO(u)))<% } %>;
    }

    private static <%= databaseType === 'no' ? asDto('User') : asEntity('User') %> getUser(Map<String, Object> details) {
        <%= databaseType === 'no' ? asDto('User') : asEntity('User') %> user = new <%= databaseType === 'no' ? asDto('User') : asEntity('User') %>();
        // handle resource server JWT, where sub claim is email and uid is ID
        if (details.get("uid") != null) {
            user.setId((String) details.get("uid"));
            user.setLogin((String) details.get("sub"));
        } else {
            user.setId((String) details.get("sub"));
        }
        if (details.get("preferred_username") != null) {
            user.setLogin(((String) details.get("preferred_username")).toLowerCase());
        } else if (user.getLogin() == null) {
            user.setLogin(user.getId());
        }
        if (details.get("given_name") != null) {
            user.setFirstName((String) details.get("given_name"));
        }
        if (details.get("family_name") != null) {
            user.setLastName((String) details.get("family_name"));
        }
        if (details.get("email_verified") != null) {
            user.setActivated((Boolean) details.get("email_verified"));
        }
        if (details.get("email") != null) {
            user.setEmail(((String) details.get("email")).toLowerCase());
        } else {
            user.setEmail((String) details.get("sub"));
        }
        if (details.get("langKey") != null) {
            user.setLangKey((String) details.get("langKey"));
        } else if (details.get("locale") != null) {
            // trim off country code if it exists
            String locale = (String) details.get("locale");
            if (locale.contains("_")) {
                locale = locale.substring(0, locale.indexOf('_'));
            } else if (locale.contains("-")) {
                locale = locale.substring(0, locale.indexOf('-'));
            }
            user.setLangKey(locale.toLowerCase());
        } else {
            // set langKey to default if not specified by IdP
            user.setLangKey(Constants.DEFAULT_LANGUAGE);
        }
        if (details.get("picture") != null) {
            user.setImageUrl((String) details.get("picture"));
        }
        user.setActivated(true);
        return user;
    }
    <%_ } _%>
    <%_ if (cacheManagerIsAvailable === true && databaseType !== 'no') { _%>

    private void clearUserCaches(<%= asEntity('User') %> user) {
        Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE)).evict(user.getLogin());
        if (user.getEmail() != null) {
            Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE)).evict(user.getEmail());
        }
    }
    <%_ } _%>
}
